Skip to main content

Troubleshooting MDX List Items Inside TabItem Causing Build Failure

This report documents a Docusaurus build failure caused by markdown list items (- ) placed inside <TabItem> JSX components across 26 .mdx files.

1) Incident Summary

  • Date: 2026-02-18
  • Symptom: build-if-changed.sh build failed with 26 MDX compilation errors.
  • Scope: 26 files in docs/visibility/Seo writing/1.content framework/.
  • Impact: Production build blocked. Site continued serving the previous build (no downtime), but new content was not deployed.

2) Confirmed Root Cause

Docusaurus MDX v2+ parser treats markdown list items (- ) as listItem nodes. When a </TabItem> JSX closing tag appears after list items inside the same <TabItem>, the parser expects the closing tag to be inside the list context, which is invalid.

Error message (repeated 26 times, one per file):

Error: MDX compilation failed for file "/app/docs/visibility/Seo writing/1.content framework/[filename].mdx"
Cause: Expected the closing tag `</TabItem>` either after the end of `listItem` (line:col)
or another opening tag after the start of `listItem` (line:col)

Why this happened:

  • The "AI Collaboration Guidelines" section in each framework file used <Tabs> with a "Do This" <TabItem> containing markdown bullet lists.
  • MDX parses - as a listItem block element, and the subsequent </TabItem> closing tag gets consumed by the list parser before the JSX parser can close the tag.
  • This is a known limitation of MDX's interaction between markdown block elements and JSX components.

3) Faulty and Fixed Content

Faulty Pattern

<Tabs>
<TabItem value="dos" label="Do This" default>

- Ask AI to brainstorm subtopics for your core topic
- Use AI for keyword research suggestions
- Have AI generate the cluster map

</TabItem>
</Tabs>

The </TabItem> on the last line is parsed as part of the list item, causing the MDX compilation error.

Applied Fix

<Tabs>
<TabItem value="dos" label="Do This" default>

&#8226; Ask AI to brainstorm subtopics for your core topic
&#8226; Use AI for keyword research suggestions
&#8226; Have AI generate the cluster map

</TabItem>
</Tabs>

Replaced markdown list markers (- ) with HTML bullet entities (&#8226; ), which renders identically but is treated as inline text, not a block-level list element.

Affected Files (26 total)

01-how-to-guide-framework.mdx listicle-framework.mdx
beginners-guide-framework.mdx pillar-page-framework.mdx
best-alternatives-framework.mdx pricing-cost-guide-framework.mdx
best-practices-framework.mdx problem-solution-framework.mdx
best-tools-framework.mdx product-review-framework.mdx
buying-guide-framework.mdx pros-and-cons-framework.mdx
case-study-framework.mdx roundup-best-of-framework.mdx
checklist-framework.mdx step-by-step-tutorial-framework.mdx
common-mistakes-framework.mdx templates-swipe-file-framework.mdx
examples-gallery-framework.mdx topic-cluster-framework.mdx
faqs-page-framework.mdx troubleshooting-guide-framework.mdx
for-beginners-explainer-framework.mdx ultimate-guide-framework.mdx
what-is-framework.mdx
x-vs-y-framework.mdx

4) Troubleshooting Steps Performed

# 1) Run build-if-changed.sh — build failed
sudo bash /home/rezriz/github/01-production/docusaurus-build-if-changed/build-if-changed.sh

# 2) Identify all failing files
sudo docker exec -w /app docusaurus npx docusaurus build 2>&1 \
| grep -E "MDX compilation failed" | head -30

# 3) Identify the error pattern (listItem + TabItem conflict)
sudo docker exec -w /app docusaurus npx docusaurus build 2>&1 \
| grep -E "(listItem|TabItem)" | head -30

# 4) Inspect specific file to confirm pattern
grep -n "^- \|TabItem" for-beginners-explainer-framework.mdx

# 5) Apply bulk fix via Python script (convert - to &#8226; inside TabItem)
for f in *.mdx; do
python3 -c "
import re
with open('$f', 'r') as fh:
content = fh.read()
lines = content.split('\n')
in_tabitem = False
new_lines = []
for line in lines:
if '<TabItem' in line.strip(): in_tabitem = True
if '</TabItem>' in line.strip(): in_tabitem = False
if in_tabitem and re.match(r'^- ', line):
new_lines.append(line.replace('- ', '&#8226; ', 1))
else:
new_lines.append(line)
result = '\n'.join(new_lines)
if result != content:
with open('$f', 'w') as fh: fh.write(result)
print(f'Fixed: $f')
"
done

# 6) Rebuild and verify
sudo bash /home/rezriz/github/01-production/docusaurus-build-if-changed/build-if-changed.sh

5) Verification After Fix

  • Build completed successfully:
    • [SUCCESS] Generated static files in "build".
  • Build validation passed:
    • [build-if-changed] Build validation passed
  • Container restarted automatically:
    • [build-if-changed] Restarting container 'docusaurus'
    • [build-if-changed] Done.
  • All 39 framework files now render correctly on the live site.

6) Prevention Checklist

  1. Never use markdown list items (- ) inside <TabItem> or other JSX components. Use HTML bullet entities (&#8226;), <ul><li> HTML tags, or tables instead.

  2. MDX JSX + Markdown rule: Block-level markdown elements (lists, blockquotes, headings) inside JSX components can cause parser conflicts. Prefer HTML equivalents inside JSX.

  3. Pre-publish build check: Always run the build before assuming content is valid:

sudo docker exec -w /app docusaurus sh -lc "npm run build"
  1. Batch content changes: When modifying many MDX files at once, test the build after the batch, not after each individual file.

  2. Quick detection command: To find future instances of this pattern:

cd /opt/docker-data/apps/docusaurus/site/docs
grep -rn "^- " --include="*.mdx" | \
while read line; do
file=$(echo "$line" | cut -d: -f1)
grep -q "TabItem" "$file" && echo "$line"
done